//此文件为管理用户信息的仓库

import { defineStore } from "pinia";
import { getToken, removeToken, setToken } from "../utils/token-utils";
import { ElMessage } from "element-plus";
import { staticRoutes, allAsyncRoutes, anyRoutes } from "@/router/routes";
import { login, getUserInfo, logout } from "@/api/user";
import type { UserInfoData } from "@/api/user";
import { cloneDeep } from "lodash";
import type { RouteRecordRaw } from "vue-router";
import router from "@/router";

// 初始化用户信息的方法
function initUserInfo(): UserInfoData {
  return {
    name: "",
    avatar: "",
    roles: [],
    routes: [],
    buttons: [],
  };
}

// 定义过滤动态路由的函数，根据权限菜单字符串数组找到 allAsyncRoutes 中的路由对象
// 第一个参数就是原异步路由对象数组
// 第二个参数就是用户的权限菜单字符串数组
function filterAsyncRoutes(
  allAsyncRoutes: RouteRecordRaw[],
  routeNames: string[]
) {
  return allAsyncRoutes.filter((item) => {
    // 如果当前 item.name 在 routeNames 数组中存在，则表示整个路由对象就是我们想要的
    // 保留满足上述条件
    // routeNames = ['Product'] / item.name = 'Product'
    // 因为item.name 有三种可能的值：string | symbol | undefined
    // 使用 ! 只能排除掉 undefined，所以，我们使用 as 断言
    if (routeNames.includes(item.name as string)) {
      // 因为子路由也是有权限控制的，所以，我们还需要对子路由中的权限进行过滤
      if (item.children && item.children.length) {
        // 如果存在子菜单，且个数大于0
        // 递归调用 filterAsyncRoutes 方法，并把结果赋值给 item.children
        // 这就是为什么我们在传入数据的时候使用了 cloneDeep 方法，因为在这里会对 children 的数据进行过滤修改
        item.children = filterAsyncRoutes(item.children, routeNames);
      }
      return true;
    }
    return false;
  });
}

// 定义注册每一个路由的函数
function addRoutes(routes: RouteRecordRaw[]) {
  routes.forEach((route) => {
    router.addRoute(route);
  });
}


// resetRouter重置路由另一种写法，清理注册的路由,移除全部路由再重新注册静态路由
// function resetRouter() {
//   // 获取当前已经注册的路由信息
//   let routes = router.getRoutes();
//   console.log(`routes`, routes);
//
//   // 使用 router 对象下的 removeRoute 方法可以删除指定name 的路由信息
//   routes.forEach((route) => {
//     // console.log(`name`, route.name);
//       router.removeRoute(route.name as string);
//   });
//
//   console.log(`最新的路由表：`, router.getRoutes())
//     // 删除所有把默认的静态路由在添加上
//     addRoutes(staticRoutes);
//     console.log(`最新的路由表：`, router.getRoutes())
// }

// 定义登出后移除非静态（meta中没有有static：true）的路由
function resetRouter() {
    // 获取当前已经注册的路由信息
    let routes = router.getRoutes();
    // 使用 router 对象下的 removeRoute 方法可以删除指定name 的路由信息
    routes.forEach((route) => {
        // console.log(`name`, route.name);
        // 通过当前路由对象下的 meta.static 的属性来判断是否需要删除
        if (!route.meta.static) {
            // 删除非 static 的路由信息
            router.removeRoute(route.name as string);
        }
    });
    // console.log(`最新的路由表：`, router.getRoutes());
}

// 定义用户信息仓库的类型
 interface UserInfoState {
    token: string;
    userInfo: UserInfoData;
    menuRoutes: RouteRecordRaw[] // 用于生成导航菜单的路由列表
}
 //****仓库****
 // 定义储存用户信息的仓库
export const useUserInfoStore = defineStore("userInfo", {
  // user 仓库中存放的数据
  state: (): UserInfoState => ({
    // 登录成功后的用户 token
    token: getToken() as string,
    userInfo: initUserInfo(),

    // 不同角色的用户将要显示的管理菜单
    // menuRoutes 将会在我们的后台管理界面的左侧显示，在 src/layout/sidebar/index.vue 文件中，会调用该数据，然后渲染菜单
    menuRoutes: staticRoutes,
  }),

  actions: {
    // 用户登录
    async login(username: string, password: string) {
      //  测试用 console.log
      //  return new Promise((resolve, reject) => {
      //     setTimeout(() => {
      //         if (username === 'admin' && password === '111111') {
      //             const token = 'token-atguigu'
      //             setToken(token)
      //             this.token = token
      //             resolve(token)
      //         } else {
      //             // 给程序使用
      //             reject(new Error('用户名或密码错误!'))
      //             // 提示用户看的
      //             ElMessage.error('用户名或密码错误!')
      //         }
      //     }, 1000)
      // })

      try {
        const res = await login(username, password);
        // 把用户登录成功后的 token 保存到 localStorage 中（持久化）
        setToken(res.token);
        // 存储 token 到 pinia
        this.token = res.token;
      } catch (e) {
        ElMessage.error("用户名或密码错误!");
        throw new Error("用户名或密码错误!");
      }
    },
    //获取用户信息
    async getInfo() {
      try {
        const res = await getUserInfo();
        console.log(`res`, res);
        // 存储后端返回的所有用户信息
        this.userInfo = res;

        // 处理当前登录用户的权限菜单，调用一个方法来实现过滤，该方法返回最终用户可访问的路由数组
        // 通过 filterAsyncRoutes 过滤得到的路由对象数组应该需要和原有的静态菜单进行合并，而不是覆盖
        let asyncRoutes = filterAsyncRoutes(
          /*待过滤的原始动态路由数组*/
          cloneDeep(allAsyncRoutes),
          /*当前用户具有的权限菜单数组*/
          this.userInfo.routes
        );
        // this.menuRoutes = staticRoutes.concat(asyncRoutes);
        this.menuRoutes = [...staticRoutes, ...asyncRoutes];
        // 这里我们现在只是把后台管理的菜单显示了出来，但是还缺少一个步骤
        // 默认情况下，router 对象注册的路由信息只包含 staticRoutes 中的路由对象，所以我们需要进行路由的动态注册
        addRoutes([...asyncRoutes, anyRoutes]);
      } catch (e) {
        throw new Error("出错了");
      }
    },
    //重置信息
    reset() {
      // 删除local中保存的token
      removeToken();
      this.token = "";
      // 提交重置用户信息的mutation
      this.userInfo = initUserInfo();
    },
    // 退出登录
    async logout() {
      try {
        // 调用后端的退出接口
        await logout();
        // 重置前端的仓库数据和localStorage中数据
        this.reset();
        // 清理登录成功后注册的路由信息
        resetRouter();
      } catch (e) {
        return Promise.reject(e);
      }
    },
  },
});
